KokaのEffect Handler
code:koka(js)
fun raise-const() : int
with ctl raise(msg) 42 // ①
8 + safe-divide(1,0) // ②
①で、raiseというeffectに対するhandlerを書いている
内容は、raise()が呼ばれたら常に42を返す、というもの
②で、実際に関数を書いている
イメージ的にはJSのtry/catchを逆の順序で書いてる
safe-divideが以下のような関数である場合
code:koka(js)
fun safe-divide( x : int, y : int ) : raise int
if y==0 then raise("div-by-zero") else x / y
上記のraise-constを実行すると、
②が実行され、1 ÷ 0でraiseが呼ばれる
safe-divideの中でraise()が呼ばれる
①のhandler内にジャンプし、42を返す
これがraise-constの最終結果になる
code:koka(js)
fun raise-const()
with ctl raise(msg) resume(42) // ③ resumeして42を返す
8 + safe-divide(1,0) // ④
上記のraise-constを実行すると、
④が実行され、1 ÷ 0でraiseが呼ばれる
safe-divideの中でraise()が呼ばれる
③のhandler内にジャンプする
③は42を渡して、safe-divideの処理を再開する
結果、safe-divideの返り値は42になり、④で8+42が計算され50を返す
これがraise-constの最終結果になる
syntax suger
以下は同じ
code:koka(js)
fun raise-const() : int
with handler
ctl raise(msg) 42
8 + safe-divide(1,0)
code:koka(js)
fun raise-const() : int
with ctl raise(msg) 42
8 + safe-divide(1,0)
文も書ける
code:koka(js)
fun ask-once() : int
var count := 0
with ctl ask()
count := count + 1
if count <= 1 then resume(42) else 0
add-twice()
条件分岐し、resumeするかどうかを決めてる
handleすることで、その関数からeffectが消えるのもポイント
fnを使うと最適化しやすくなるらしい
ただの糖衣構文ではない
e.g. st, io, console
masking effects
普通にやると
code:koka(js)
effect fun emit( msg : string ) : ()
fun mask-emit()
with fun emit(msg) println("outer:" ++ msg)
with fun emit(msg) println("inner:" ++ msg)
emit("hi")
emit("there")
code:result
inner:hi
inner:there
当然、内側のhandlerにのみ捕捉される
maskする
code:koka(js)
fun mask-emit()
with fun emit(msg) println("outer:" ++ msg)
with fun emit(msg) println("inner:" ++ msg)
emit("hi")
mask<emit>
emit("there")
最も近いhandlerをmaskすることで、外側のhandlerに捕捉させる
code:;result
inner:hi
outer:there